 /*******************************************************************************
  * Copyright (c) 2005, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.internal.handlers;

 import org.eclipse.core.commands.Command;
 import org.eclipse.core.commands.CommandEvent;
 import org.eclipse.core.commands.ExecutionEvent;
 import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.core.commands.ICommandListener;
 import org.eclipse.core.commands.INamedHandleStateIds;
 import org.eclipse.core.commands.NotHandledException;
 import org.eclipse.core.commands.ParameterizedCommand;
 import org.eclipse.core.commands.State;
 import org.eclipse.core.commands.common.NotDefinedException;
 import org.eclipse.jface.action.AbstractAction;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.IMenuCreator;
 import org.eclipse.jface.bindings.TriggerSequence;
 import org.eclipse.jface.bindings.keys.KeySequence;
 import org.eclipse.jface.bindings.keys.KeyStroke;
 import org.eclipse.jface.commands.RadioState;
 import org.eclipse.jface.commands.ToggleState;
 import org.eclipse.jface.menus.IMenuStateIds;
 import org.eclipse.jface.menus.TextState;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.util.Util;
 import org.eclipse.swt.events.HelpListener;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.ui.commands.ICommandService;
 import org.eclipse.ui.internal.commands.CommandImageManager;
 import org.eclipse.ui.internal.commands.ICommandImageService;
 import org.eclipse.ui.keys.IBindingService;
 import org.eclipse.ui.services.IServiceLocator;

 /**
  * <p>
  * A wrapper around the new command infrastructure that imitates the old
  * <code>IAction</code> interface.
  * </p>
  * <p>
  * Clients may instantiate this class, but must not extend.
  * </p>
  * <p>
  * <strong>PROVISIONAL</strong>. This class or interface has been added as
  * part of a work in progress. There is a guarantee neither that this API will
  * work nor that it will remain the same. Please do not use this API without
  * consulting with the Platform/UI team.
  * </p>
  * <p>
  * This class is eventually intended to exist in
  * <code>org.eclipse.ui.handlers</code>.
  * </p>
  *
  * @since 3.2
  */
 public final class CommandLegacyActionWrapper extends AbstractAction {

     /**
      * Listens to changes to one or more commands, and forwards them out through
      * the property change event mechanism.
      */
     private final class CommandListener implements ICommandListener {
         public final void commandChanged(final CommandEvent commandEvent) {
             final Command baseCommand = commandEvent.getCommand();

             // Check if the name changed.
 if (commandEvent.isNameChanged()) {
                 String newName = null;
                 if (baseCommand.isDefined()) {
                     try {
                         newName = baseCommand.getName();
                     } catch (final NotDefinedException e) {
                         // Not defined, so leave as null.
 }
                 }
                 firePropertyChange(IAction.TEXT, null, newName);
             }

             // Check if the description changed.
 if (commandEvent.isDescriptionChanged()) {
                 String newDescription = null;
                 if (baseCommand.isDefined()) {
                     try {
                         newDescription = baseCommand.getDescription();
                     } catch (final NotDefinedException e) {
                         // Not defined, so leave as null.
 }
                 }
                 firePropertyChange(IAction.DESCRIPTION, null, newDescription);
                 firePropertyChange(IAction.TOOL_TIP_TEXT, null, newDescription);
             }

             // Check if the handled property changed.
 if (commandEvent.isHandledChanged()) {
                 if (baseCommand.isHandled()) {
                     firePropertyChange(IAction.HANDLED, Boolean.FALSE,
                             Boolean.TRUE);
                 } else {
                     firePropertyChange(IAction.HANDLED, Boolean.TRUE,
                             Boolean.FALSE);
                 }
             }
         }

     }

     /**
      * The command with which this action is associated; never <code>null</code>.
      */
     private ParameterizedCommand command;

     /**
      * Listens to changes in a command, and forwards them out through the
      * property change event mechanism.
      */
     private final ICommandListener commandListener = new CommandListener();

     /**
      * Whether this action has been marked as enabled.
      */
     private boolean enabled = true;

     /**
      * The identifier for the action. This may be <code>null</code>.
      */
     private String id;

     /**
      * A service locator that can be used for retrieving command-based services.
      * This value is never <code>null</code>.
      */
     private final IServiceLocator serviceLocator;

     /**
      * The image style to use for this action. This value may be
      * <code>null</code>.
      */
     private final String style;

     /**
      * Constructs a new instance of <code>ActionProxy</code>.
      *
      * @param id
      * The initial action identifier; may be <code>null</code>.
      * @param command
      * The command with which this action is associated; must not be
      * <code>null</code>.
      * @param style
      * The image style to use for this action, may be
      * <code>null</code>.
      * @param serviceLocator
      * A service locator that can be used to find various
      * command-based services; must not be <code>null</code>.
      */
     public CommandLegacyActionWrapper(final String id,
             final ParameterizedCommand command, final String style,
             final IServiceLocator serviceLocator) {
         if (command == null) {
             throw new NullPointerException (
                     "An action proxy can't be created without a command"); //$NON-NLS-1$
 }

         if (serviceLocator == null) {
             throw new NullPointerException (
                     "An action proxy can't be created without a service locator"); //$NON-NLS-1$
 }

         this.command = command;
         this.id = id;
         this.style = style;
         this.serviceLocator = serviceLocator;

         // TODO Needs to listen to command, state, binding and image changes.
 command.getCommand().addCommandListener(commandListener);
     }

     public final int getAccelerator() {
         final String commandId = getActionDefinitionId();
         final IBindingService bindingService = (IBindingService) serviceLocator
                 .getService(IBindingService.class);
         final TriggerSequence triggerSequence = bindingService
                 .getBestActiveBindingFor(commandId);
         if (triggerSequence instanceof KeySequence) {
             final KeySequence keySequence = (KeySequence) triggerSequence;
             final KeyStroke[] keyStrokes = keySequence.getKeyStrokes();
             if (keyStrokes.length == 1) {
                 final KeyStroke keyStroke = keyStrokes[0];
                 return keyStroke.getModifierKeys() | keyStroke.getNaturalKey();
             }
         }

         return 0;
     }

     public final String getActionDefinitionId() {
         return command.getId();
     }

     public final String getDescription() {
         try {
             return command.getCommand().getDescription();
         } catch (final NotDefinedException e) {
             return null;
         }
     }

     public final ImageDescriptor getDisabledImageDescriptor() {
         final String commandId = getActionDefinitionId();
         final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
                 .getService(ICommandImageService.class);
         return commandImageService.getImageDescriptor(commandId,
                 CommandImageManager.TYPE_DISABLED, style);
     }

     public final HelpListener getHelpListener() {
         // TODO Help. Addressing help on commands.
 return null;
     }

     public final ImageDescriptor getHoverImageDescriptor() {
         final String commandId = getActionDefinitionId();
         final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
                 .getService(ICommandImageService.class);
         return commandImageService.getImageDescriptor(commandId,
                 CommandImageManager.TYPE_HOVER, style);
     }

     public final String getId() {
         return id;
     }

     public final ImageDescriptor getImageDescriptor() {
         final String commandId = getActionDefinitionId();
         final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
                 .getService(ICommandImageService.class);
         return commandImageService.getImageDescriptor(commandId, style);
     }

     public final IMenuCreator getMenuCreator() {
         // TODO Pulldown. What kind of callback is needed here?
 return null;
     }

     public final int getStyle() {
         // TODO Pulldown. This does not currently support the pulldown style.
 final State state = command.getCommand().getState(IMenuStateIds.STYLE);
         if (state instanceof RadioState) {
             return IAction.AS_RADIO_BUTTON;
         } else if (state instanceof ToggleState) {
             return IAction.AS_CHECK_BOX;
         }

         return IAction.AS_PUSH_BUTTON;
     }

     public final String getText() {
         try {
             return command.getName();
         } catch (final NotDefinedException e) {
             return null;
         }
     }

     public final String getToolTipText() {
         return getDescription();
     }

     public final boolean isChecked() {
         final State state = command.getCommand().getState(IMenuStateIds.STYLE);
         if (state instanceof ToggleState) {
             final Boolean currentValue = (Boolean ) state.getValue();
             return currentValue.booleanValue();
         }

         return false;
     }

     public final boolean isEnabled() {
         final Command baseCommand = command.getCommand();
         return baseCommand.isEnabled() && enabled;
     }

     /**
      * Whether this action's local <code>enabled</code> property is set. This
      * can be used by handlers that are trying to check if
      * {@link #setEnabled(boolean)} has been called. This is typically used by
      * legacy action proxies who are trying to avoid a <a
      * HREF="https://bugs.eclipse.org/bugs/show_bug.cgi?id=117496">stack
      * overflow</a>.
      *
      * @return <code>false</code> if someone has called
      * {@link #setEnabled(boolean)} with <code>false</code>;
      * <code>true</code> otherwise.
      */
     public final boolean isEnabledDisregardingCommand() {
         return enabled;
     }

     public final boolean isHandled() {
         final Command baseCommand = command.getCommand();
         return baseCommand.isHandled();
     }

     public final void run() {
         runWithEvent(null);
     }

     public final void runWithEvent(final Event event) {
         final Command baseCommand = command.getCommand();
         final ExecutionEvent executionEvent = new ExecutionEvent(command
                 .getCommand(), command.getParameterMap(), event, null);
         try {
             baseCommand.execute(executionEvent);
             firePropertyChange(IAction.RESULT, null, Boolean.TRUE);

         } catch (final NotHandledException e) {
             firePropertyChange(IAction.RESULT, null, Boolean.FALSE);

         } catch (final ExecutionException e) {
             firePropertyChange(IAction.RESULT, null, Boolean.FALSE);
             // TODO Should this be logged?

         }
     }

     public final void setAccelerator(final int keycode) {
         // TODO Binding. This is hopefully not essential.
 }

     public final void setActionDefinitionId(final String id) {
         // Get the old values.
 final boolean oldChecked = isChecked();
         final String oldDescription = getDescription();
         final boolean oldEnabled = isEnabled();
         final boolean oldHandled = isHandled();
         final ImageDescriptor oldDefaultImage = getImageDescriptor();
         final ImageDescriptor oldDisabledImage = getDisabledImageDescriptor();
         final ImageDescriptor oldHoverImage = getHoverImageDescriptor();
         final String oldText = getText();

         // Update the command.
 final Command oldBaseCommand = command.getCommand();
         oldBaseCommand.removeCommandListener(commandListener);
         final ICommandService commandService = (ICommandService) serviceLocator
                 .getService(ICommandService.class);
         final Command newBaseCommand = commandService.getCommand(id);
         command = new ParameterizedCommand(newBaseCommand, null);
         newBaseCommand.addCommandListener(commandListener);

         // Get the new values.
 final boolean newChecked = isChecked();
         final String newDescription = getDescription();
         final boolean newEnabled = isEnabled();
         final boolean newHandled = isHandled();
         final ImageDescriptor newDefaultImage = getImageDescriptor();
         final ImageDescriptor newDisabledImage = getDisabledImageDescriptor();
         final ImageDescriptor newHoverImage = getHoverImageDescriptor();
         final String newText = getText();

         // Fire property change events, as necessary.
 if (newChecked != oldChecked) {
             if (oldChecked) {
                 firePropertyChange(IAction.CHECKED, Boolean.TRUE, Boolean.FALSE);
             } else {
                 firePropertyChange(IAction.CHECKED, Boolean.FALSE, Boolean.TRUE);
             }
         }

         if (!Util.equals(oldDescription, newDescription)) {
             firePropertyChange(IAction.DESCRIPTION, oldDescription,
                     newDescription);
             firePropertyChange(IAction.TOOL_TIP_TEXT, oldDescription,
                     newDescription);
         }

         if (newEnabled != oldEnabled) {
             if (oldEnabled) {
                 firePropertyChange(IAction.ENABLED, Boolean.TRUE, Boolean.FALSE);
             } else {
                 firePropertyChange(IAction.ENABLED, Boolean.FALSE, Boolean.TRUE);
             }
         }

         if (newHandled != oldHandled) {
             if (oldHandled) {
                 firePropertyChange(IAction.HANDLED, Boolean.TRUE, Boolean.FALSE);
             } else {
                 firePropertyChange(IAction.HANDLED, Boolean.FALSE, Boolean.TRUE);
             }
         }

         if (!Util.equals(oldDefaultImage, newDefaultImage)) {
             firePropertyChange(IAction.IMAGE, oldDefaultImage, newDefaultImage);
         }

         if (!Util.equals(oldDisabledImage, newDisabledImage)) {
             firePropertyChange(IAction.IMAGE, oldDisabledImage,
                     newDisabledImage);
         }

         if (!Util.equals(oldHoverImage, newHoverImage)) {
             firePropertyChange(IAction.IMAGE, oldHoverImage, newHoverImage);
         }

         if (!Util.equals(oldText, newText)) {
             firePropertyChange(IAction.TEXT, oldText, newText);
         }
     }

     public final void setChecked(final boolean checked) {
         final State state = command.getCommand().getState(IMenuStateIds.STYLE);
         if (state instanceof ToggleState) {
             final Boolean currentValue = (Boolean ) state.getValue();
             if (checked != currentValue.booleanValue()) {
                 if (checked) {
                     state.setValue(Boolean.TRUE);
                 } else {
                     state.setValue(Boolean.FALSE);
                 }
             }
         }
     }

     public final void setDescription(final String text) {
         final State state = command.getCommand().getState(
                 INamedHandleStateIds.DESCRIPTION);
         if (state instanceof TextState) {
             final String currentValue = (String ) state.getValue();
             if (!Util.equals(text, currentValue)) {
                 state.setValue(text);
             }
         }
     }

     public final void setDisabledImageDescriptor(final ImageDescriptor newImage) {
         final String commandId = getActionDefinitionId();
         final int type = CommandImageManager.TYPE_DISABLED;
         final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
                 .getService(ICommandImageService.class);
         commandImageService.bind(commandId, type, style, newImage);
     }

     public final void setEnabled(final boolean enabled) {
         if (enabled != this.enabled) {
             final Boolean oldValue = this.enabled ? Boolean.TRUE
                     : Boolean.FALSE;
             final Boolean newValue = enabled ? Boolean.TRUE : Boolean.FALSE;
             this.enabled = enabled;
             firePropertyChange(ENABLED, oldValue, newValue);
         }
     }

     public final void setHelpListener(final HelpListener listener) {
         // TODO Help Haven't even started to look at help yet.

     }

     public final void setHoverImageDescriptor(final ImageDescriptor newImage) {
         final String commandId = getActionDefinitionId();
         final int type = CommandImageManager.TYPE_HOVER;
         final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
                 .getService(ICommandImageService.class);
         commandImageService.bind(commandId, type, style, newImage);
     }

     public final void setId(final String id) {
         this.id = id;
     }

     public final void setImageDescriptor(final ImageDescriptor newImage) {
         final String commandId = getActionDefinitionId();
         final int type = CommandImageManager.TYPE_DEFAULT;
         final ICommandImageService commandImageService = (ICommandImageService) serviceLocator
                 .getService(ICommandImageService.class);
         commandImageService.bind(commandId, type, style, newImage);
     }

     public final void setMenuCreator(final IMenuCreator creator) {
         // TODO Pulldown. This is complicated
 }

     public final void setText(final String text) {
         final State state = command.getCommand().getState(
                 INamedHandleStateIds.NAME);
         if (state instanceof TextState) {
             final String currentValue = (String ) state.getValue();
             if (!Util.equals(text, currentValue)) {
                 state.setValue(text);
             }
         }
     }

     public final void setToolTipText(final String text) {
         setDescription(text);
     }

 }

